﻿using System;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Index.Bintree
{
	[Serializable]
	public class Node<T> : NodeBase<T>
	{
		public static Node<T> CreateNode(Interval itemInterval)
		{
			Key key = new Key(itemInterval);
			return new Node<T>(key.Interval, key.Level);
		}

		public static Node<T> CreateExpanded(Node<T> node, Interval addInterval)
		{
			Interval interval = new Interval(addInterval);
			if (node != null)
			{
				interval.ExpandToInclude(node._interval);
			}
			Node<T> node2 = Node<T>.CreateNode(interval);
			if (node != null)
			{
				node2.Insert(node);
			}
			return node2;
		}

		public Node(Interval interval, int level)
		{
			this._interval = interval;
			this._level = level;
			this._centre = (interval.Min + interval.Max) / 2.0;
		}

		public Interval Interval
		{
			get
			{
				return this._interval;
			}
		}

		protected override bool IsSearchMatch(Interval itemInterval)
		{
			return itemInterval.Overlaps(this._interval);
		}

		public Node<T> GetNode(Interval searchInterval)
		{
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(searchInterval, this._centre);
			if (subnodeIndex != -1)
			{
				return this.GetSubnode(subnodeIndex).GetNode(searchInterval);
			}
			return this;
		}

		public NodeBase<T> Find(Interval searchInterval)
		{
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(searchInterval, this._centre);
			if (subnodeIndex == -1)
			{
				return this;
			}
			if (this.Subnode[subnodeIndex] != null)
			{
				return this.Subnode[subnodeIndex].Find(searchInterval);
			}
			return this;
		}

		public void Insert(Node<T> node)
		{
			Assert.IsTrue(this._interval == null || this._interval.Contains(node.Interval));
			int subnodeIndex = NodeBase<T>.GetSubnodeIndex(node._interval, this._centre);
			if (node._level == this._level - 1)
			{
				this.Subnode[subnodeIndex] = node;
				return;
			}
			Node<T> node2 = this.CreateSubnode(subnodeIndex);
			node2.Insert(node);
			this.Subnode[subnodeIndex] = node2;
		}

		private Node<T> GetSubnode(int index)
		{
			if (this.Subnode[index] == null)
			{
				this.Subnode[index] = this.CreateSubnode(index);
			}
			return this.Subnode[index];
		}

		private Node<T> CreateSubnode(int index)
		{
			double min = 0.0;
			double max = 0.0;
			if (index != 0)
			{
				if (index == 1)
				{
					min = this._centre;
					max = this._interval.Max;
				}
			}
			else
			{
				min = this._interval.Min;
				max = this._centre;
			}
			return new Node<T>(new Interval(min, max), this._level - 1);
		}

		private readonly Interval _interval;

		private readonly double _centre;

		private readonly int _level;
	}
}
